/* Copyright (c) 2015,2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.jar;

import java.io.File;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.cert.X509Certificate;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 加密机接口
 */
public abstract class Encryptor
{
	private static final Logger logger = LogManager.getLogger();

	protected static Encryptor encryptor = null;

	public static final int MAC_MODE_ECB    = 1;
	public static final int MAC_MODE_CUP    = 2;
	public static final int MAC_MODE_AX9_9  = 3;
	public static final int MAC_MODE_XOR    = 4;
	public static final int MAC_MODE_AX9_19 = 5;

	public static final int TRACK_2 = 1;
	public static final int TRACK_3 = 2;

	// Web 系统加密使用的密钥
	public static final String WEB_SKEY = "web_skey";

	/**
	 * 获取加密机实例
	 *
	 * @return 加密机实例
	 */
	public static Encryptor getInstance() {
		if (encryptor != null) {
			return encryptor;
		}
		String name = "null";
		if (name.equalsIgnoreCase("null")) {
			encryptor = new NullEncryptor();
		} else {
			logger.warn("系统不支持加密机[{}]，将默认使用软件加密机.", name);
			encryptor = new NullEncryptor();
		}
		if (!encryptor.init()) {
			logger.error("初始化加密机[{}]失败.", name);
			encryptor = null;
		}
		return encryptor;
	}

	/**
	 * 获取 MAC 算法名称
	 */
	public static String macModeName(int mode) {
		switch (mode) {
		case MAC_MODE_ECB:
			return "ECB";
		case MAC_MODE_CUP:
			return "CUP";
		case MAC_MODE_AX9_9:
			return "X9.9";
		case MAC_MODE_AX9_19:
			return "X9.19";
		case MAC_MODE_XOR:
			return "XOR";
		default:
			return "未知";
		}
	}

	/**
	 * 新建加密机对象, 这个函数不能公开访问
	 */
	protected Encryptor() {
	}

	/**
	 * 初始化加密机
	 *
	 * @return 成功/失败
	 */
	abstract protected boolean init();

	/**
	 * 运行时添加新的证书
	 *
	 * @param cert_file 证书文件
	 * @param passwd 解密密码
	 * @return 成功或失败
	 */
	abstract public boolean addPKCS12Cert(File cert_file,
			String passwd, String alias);
	abstract public boolean addX509Cert(File cert_file, String alias);

	/**
	 * 检查指定的别名是否存在
	 *
	 * @param alias 别名
	 * @return 是否存在
	 */
	abstract public boolean contain(String alias);

	/**
	 * 删除一项
	 */
	abstract public void deleteByAlias(String alias);

	/**
	 * 获取存储的安全密钥
	 *
	 * @param index 密钥别名
	 * @return 存储的原始密钥
	 */
	abstract public byte[] getSecretKey(String key_name);

	/**
	 * 取成员主密钥，成员主密钥采用固定的索引.
	 */
	abstract public byte[] getMasterKey();

	/**
	 * 从加密机中获取 X.509 公钥证书
	 *
	 * @param index 别名
	 * @return 公钥证书，或 null
	 */
	abstract public X509Certificate getX509Certificate(String index);

	/**
	 * 从加密机中获取私钥
	 *
	 * @param index 别名
	 * @return 私钥，或 null
	 */
	abstract public PrivateKeyEntry getPrivateKeyEntry(String index);

	/**
	 * 通过指定安全密钥解密数据，采用 DES/3DES 算法
	 *
	 * @param key_name 安全密钥编号
	 * @param key 待解密数据
	 * @return 解密后数据
	 */
	abstract public byte[] decrypt(String key_name, byte[] key);
	abstract public byte[] decrypt(String key_name, String key);

	/**
	 * 通过安全密钥加密数据，采用 DES/3DES 算法
	 *
	 * @param key_name 安全密钥编号
	 * @param key 待加密数据
	 * @return 加密后数据
	 */
	abstract public byte[] encrypt(String key_name, byte[] key);
	abstract public byte[] encrypt(String key_name, String key);

	/**
	 * 计算机构主密钥与第二个值的异或结果
	 *
	 * @param key_name 安全密钥编号
	 * @param value 待计算值
	 * @return 计算结果
	 */
	abstract public byte[] xor(String key_name, byte[] value);

	/**
	 * 基于提供的数据以及密钥计算 MAC
	 *
	 * @param key_name 主密钥编号
	 * @param mode MAC 加密模式
	 * @param mackey 加密后的 MAC 密钥
	 * @param macbuf 生成 MAC 的输入数据
	 * @return 8 字节 MAC 字节流，或 null
	 */
	abstract public byte[] genMAC(String key_name, int mode,
			String mackey, byte[] macbuf);
	abstract public byte[] genMAC(String key_name, int mode,
			byte[] mackey, byte[] macbuf);

	/**
	 * 使用未加密的 mackey 计算 MAC
	 *
	 * @param mode MAC 加密模式
	 * @param mackey 未加密的 MAC 密钥
	 * @param macbuf 生成 MAC 的输入数据
	 * @return 8 字节 MAC 字节流，或 null
	 */
	abstract public byte[] genMAC(int mode, byte[] mackey, byte[] macbuf);

	/**
	 * 加/解密二、三磁道数据
	 *
	 * @param track 哪个磁道
	 * @param tdk 磁道加密密钥
	 * @param bytes 磁道数据
	 * @return 解密后磁道数据
	 */
	abstract public byte[] encryptTrack(int track, byte[] tdk, byte[] bytes);
	abstract public byte[] decryptTrack(int track, byte[] tdk, byte[] bytes);

	/**
	 * 加/解密 PIN
	 */
	abstract public byte[] encryptPin(byte[] pik, byte[] pin, byte[] pan);
	abstract public byte[] decryptPin(byte[] pik, byte[] pin, byte[] pan);

	/**
	 * 计算 SHA1 摘要码
	 *
	 * @param input 输入数据
	 * @param offset 输入数据起始偏移
	 * @param length 输入数据长度，小于或等于 0 表示取 data.length
	 * @return 摘要代码
	 */
	abstract public byte[] sha1(byte[] input, int offset, int length);
	abstract public byte[] sha1(byte[] input);

	/**
	 * 计算 SHA1withRSA 数字签名
	 *
	 * @param key_name 私钥别名
	 * @param input 签名数据
	 * @param offset 输入数据偏移
	 * @param length 输入数据长度
	 * @return 签名
	 */
	abstract public byte[] signSHA1WithRSA(String key_name, byte[] input,
			int offset, int length);
	abstract public byte[] signSHA1WithRSA(String key_name, byte[] input);

	/**
	 * 计算 MD5withRSA 数字签名
	 *
	 * @param key_name 私钥别名
	 * @param input 签名数据
	 * @param offset 输入数据偏移
	 * @param length 输入数据长度
	 * @return 签名
	 */
	abstract public byte[] signMD5WithRSA(String key_name, byte[] input,
			int offset, int length);
	abstract public byte[] signMD5WithRSA(String key_name, byte[] input);

	/**
	 * 验证签名，使用 SHA1withRSA 算法
	 *
	 * @param cert_serial 公钥证书序号，不是别名
	 * @param input 输入数据
	 * @param to_verify 待验证数据
	 * @return 成功或失败
	 */
	abstract public boolean verifySignSHA1WithRSA(String cert_serial,
			byte[] input, byte[] to_verify);

	/**
	 * 验证签名，使用 SHA1withRSA 算法
	 *
	 * @param cert_serial 公钥证书别名
	 * @param input 输入数据
	 * @param to_verify 待验证数据
	 * @return 成功或失败
	 */
	abstract public boolean verifySignSHA1WithRSAByAlias(String key_name,
			byte[] input, byte[] to_verify);

	/**
	 * 验证签名，使用 MD5withRSA 算法
	 *
	 * @param cert_serial 公钥证书序号，不是别名
	 * @param input 输入数据
	 * @param to_verify 待验证数据
	 * @return 成功或失败
	 */
	abstract public boolean verifySignMD5WithRSA(String cert_serial,
			byte[] input, byte[] to_verify);

	/**
	 * 验证签名，使用 MD5withRSA 算法
	 *
	 * @param cert_serial 公钥证书别名
	 * @param input 输入数据
	 * @param to_verify 待验证数据
	 * @return 成功或失败
	 */
	abstract public boolean verifySignMD5WithRSAByAlias(String key_name,
			byte[] input, byte[] to_verify);

	/**
	 * 用于加密全渠道账号、手机号码等等
	 *
	 * @param key_name: 公钥证书名
	 *
	 * @return 加密数据
	 */
	abstract public String AcpEncrypt(String key_name, byte[] data_clear);

	/**
	 * 用于解密全渠道账号、手机号码等等
	 *
	 * @param key_name: 公钥证书名
	 *
	 * @return 解密数据
	 */
	abstract public byte[] AcpDecrypt(String key_name, byte[] data_cipher);

	/**
	 * 用于加密全渠道敏感账户信息包括PIN、磁道信息(含芯片等效磁道信息)、CVN2和卡片有效期等等
	 *
	 * @param key_name: 公钥证书名
	 * @param pin: 待加密 PIN 数据
	 *
	 * @return 加密后 PIN 数据
	 */
	abstract public byte[] AcpEncryptPin(String key_name, byte[] pin_clear);

	/**
	 * 用于解密全渠道敏感账户信息包括PIN、磁道信息(含芯片等效磁道信息)、CVN2和卡片有效期等等
	 *
	 * @param key_name: 私钥证书名
	 *
	 * @return 解密数据
	 */
	abstract public byte[] AcpPKDeccryptPin(String key_name, byte[] pin_cipher);
}
